#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/proc_fs.h> // [2]

#define BUFFER "Hello World!\n"

// static struct proc_dir_entry *proc_file;
static struct  {
    struct proc_dir_entry *proc_file;
    char proc_filename[15];
} proc_entry_wrap ;
static int proc_file_reg;

static struct proc_ops proc_file_fops;
static char proc_file_default_name[] = "hello";
static umode_t proc_file_default_mode = 0666;

// ------------------- procfs 操作函数
// [3]

static ssize_t procfs_hello_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) {
    int ret;

    // 向用户空间拷贝数据
    ret = copy_to_user(buf, BUFFER, strlen(BUFFER));
    if (ret < 0) {
        printk(KERN_ERR "copy_to_user failed\n");
        return -EFAULT;
    }

    return strlen(BUFFER);
}


// ------------------- procfs 初始化创建文件节点函数
// [2]

static int procfs_hello_init(char *filename, umode_t mode)
{
    int ret = 0;

    // 初始化 proc_fops
    proc_file_fops.proc_read = procfs_hello_read;

    // 创建 /proc/hello 文件
    proc_entry_wrap.proc_file = proc_create(filename, mode, NULL, &proc_file_fops);
    if (proc_entry_wrap.proc_file == NULL) {
        printk(KERN_ERR "proc_create failed\n");
        ret = -ENOMEM;
        goto fail;
    }

    proc_file_reg = 1;
    printk(KERN_INFO "proc_create: create /proc/%s successfully\n", filename);
    strcpy(proc_entry_wrap.proc_filename, filename);

fail:
    return ret;
}

static void procfs_hello_exit(void)
{
    // 删除 /proc/hello 文件
    if (proc_file_reg) {
        proc_remove(proc_entry_wrap.proc_file);
        proc_entry_wrap.proc_file = NULL;
        
        proc_file_reg = 0;
        printk(KERN_INFO "proc_remove: remove /proc/%s successfully\n", proc_entry_wrap.proc_filename);
    }
}

// ------------------- 初始化函数
// [1]

static int __init hello_init(void)
{
    int ret = 0;
    
    ret = procfs_hello_init(proc_file_default_name, proc_file_default_mode);
    if (ret < 0) {
        printk(KERN_ERR "procfs_hello_init failed\n");
    }

    return ret;
}

static void __exit hello_exit(void)
{
    if (proc_file_reg) {
        procfs_hello_exit();
    }
}

// ----------- 向内核注册 模块 初始化/退出函数
module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");